
import java.util.ArrayList;

import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MetaEventListener;
import javax.sound.midi.MetaMessage;
import javax.sound.midi.MidiEvent;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;
import javax.sound.midi.ShortMessage;
import javax.sound.midi.Track;

public class BeatModel implements BeatModelInterface, MetaEventListener {
	
	Sequencer sequencer ; 
	
	Sequence sequence ; 
	Track track ; 
	
	ArrayList beatObservers = new ArrayList() ;
	ArrayList bpmObservers = new ArrayList();

	private int bpm; 

	public void initialize() 
	{
		setUpMidi(); 
		buildTrackAndStart(); 		
	}

	private void buildTrackAndStart()  {
		// TODO Auto-generated method stub
		int [] trackList = { 35, 0, 46, 0 } ; 
		
		sequence.deleteTrack(null);
		track = sequence.createTrack(); 
		
		// Make Track for me
		for ( int xKey = 0 ; xKey < trackList.length ; xKey++) {
			int key = trackList[xKey] ; 
			if ( key != 0 ) {
				track.add( makeEvent(144,9,key,100,xKey)); 
				track.add( makeEvent(128,9,key,100,xKey+1));
			}
		}
		
		track.add(makeEvent(192,9,1,0,4));
		try {
			sequencer.setSequence(sequence);
		} catch (InvalidMidiDataException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}

	private MidiEvent makeEvent(int comd, int channel, int one, int two, int tick) {
		MidiEvent event = null ; 
	
		try {
			ShortMessage msg = new ShortMessage();
			msg.setMessage(comd, channel, one, two);
			event = new MidiEvent(msg, tick);
			
		} catch (InvalidMidiDataException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return event;
	}

	private void setUpMidi() {
		// TODO Auto-generated method stub
		try {
			sequencer = MidiSystem.getSequencer();

			sequencer.open();
			sequencer.addMetaEventListener(this);
			sequence = new Sequence(Sequence.PPQ, 4); 
			track = sequence.createTrack(); 
			sequencer.setTempoInBPM(getBPM());
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
	}

	public void on() {
		sequencer.start();
		setBPM(90); 		
	}

	public void off() {
		setBPM(0);
		sequencer.stop(); 		
	}

	public void setBPM(int bpm) {
		this.bpm = bpm ;
		
		sequencer.setTempoInBPM(getBPM());
		notifyBPMObservers(); 
		beatEvent();
		
	}
	
	public void notifyBPMObservers()
	{
		for(int i=0;i<bpmObservers.size();i++)
		{
			BPMObserver observer =(BPMObserver)bpmObservers.get(i);
			observer.updateBPM();
		}
	}
	

	public void notifybeatObservers()
	{
		for(int i=0;i<beatObservers.size();i++)
		{
			BeatObserver observer =(BeatObserver)beatObservers.get(i);
			observer.updateBeat();
		}
	}

	public int getBPM() {
		// TODO Auto-generated method stub
		return bpm;
	}

	
	public void registerObserver(BeatObserver observer) 
	{
		// TODO Auto-generated method stub
		beatObservers.add(observer);
	}

	public void removeObserver(BeatObserver observer) 
	{
		// TODO Auto-generated method stub
		int i=beatObservers.indexOf(observer);
		if (i >=0)
		{
			beatObservers.remove(i);
		}
	}

	public void registerObserver(BPMObserver observer) 
	{
		// TODO Auto-generated method stub
		bpmObservers.add(observer);
	}

	public void removeObserver(BPMObserver observer) 
	{
		// TODO Auto-generated method stub
		int i=bpmObservers.indexOf(observer);
		if (i >=0)
		{
			bpmObservers.remove(i);
		}
	}
	
	void beatEvent()
	{
		notifybeatObservers();
	}
	
	public void meta(MetaMessage arg0) 
	{
		// TODO Auto-generated method stub
		if (arg0.getType() == 47)
		{
			beatEvent();
			sequencer.start();
			setBPM(getBPM());
		}
	}

	
}
